﻿using System;
using System.IO;
using System.Runtime.CompilerServices;

namespace LINQPadLogParser
{
  internal sealed class CachedStream
  {
    public const long PageSize = 64*1024;
    private readonly CacheItem[] _cache = new CacheItem[10];
    private readonly long _from;
    private readonly FileStream _stream;
    private readonly long _to;
    private int _cachePosition;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public CachedStream(string path, long from = 0, long to = -1)
    {
      _stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
      for (int i = 0; i < _cache.Length; i++)
      {
        _cache[i] = new CacheItem(-1, null);
      }
      _from = from;
      _to = to > -1 ? to : _stream.Length;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public byte[] GetPage(int number)
    {
      CacheItem prev = _cache[_cachePosition];
      if (prev.Id == number)
      {
        return prev.Page;
      }
      foreach (CacheItem t in _cache)
      {
        if (t.Id == number)
        {
          return t.Page;
        }
      }
      long offset = PageSize*number + _from;
      long bufferSize = Math.Min(_to - offset, PageSize);
      if (bufferSize <= 0)
      {
        return null;
      }
      _cachePosition = (_cachePosition + 1)%_cache.Length;
      byte[] buffer = _cache[_cachePosition].Page;
      if (buffer == null || buffer.Length != bufferSize)
      {
        buffer = new byte[bufferSize];
      }
#if SpeedTest
      Interlocked.Increment(ref LogParser.Reads);
#endif
      _stream.Position = offset;
      _stream.Read(buffer, 0, buffer.Length);
      CacheItem cache = _cache[_cachePosition];
      cache.Id = number;
      cache.Page = buffer;
      return buffer;
    }

    private sealed class CacheItem
    {
      public int Id;
      public byte[] Page;

      [MethodImpl(MethodImplOptions.AggressiveInlining)]
      public CacheItem(int id, byte[] page)
      {
        Id = id;
        Page = page;
      }
    }
  }
}